package com.jdk.learn.java8.stream;

import com.alibaba.fastjson.JSONObject;
import com.jdk.learn.java8.bo.Apple;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
 * Stream流
 * ---------------------
 * 创建的几种方式
 *  1. 通过集合list.stream
 *  2. 通过values Stream.of
 *  3. 通过数组 Arrays.stream
 *  4. 通过文件 Files.lines
 *  5. 通过迭代器 Stream.iterate
 *  6. 通过生成器 Stream.generate
 * ---------------------
 * 常用操作
 *  filter 过滤
 *   map  转换
 *   flatMap
 *   distinct 去重
 *   limit  限制
 *   groupBy 分组
 *   toCollect 转为集合
 *   skip 跳过
 *   find 查找
 *   match 匹配
 *   reduce 归约操作
 *   foreach 循环
 *
 * -------------------------
 * 注意事项：
 *  stream流只可被操作一次
 *  操作分为中断操作和中间操作
 *      中间操作：filter、distinct、skip、limit、map、flatMap、sorted
 *      中断操作：anyMatch、noneMatch、allMatch、findAny、findFirst、forEach、collect、reduce、count
 *
 *--------------------------
 * @author bgt
 * @version V1.0
 * @since 2019-01-07 10:32
 */
public class StreamLearn {
    public static List<Apple> apples=Arrays.asList(new Apple("ricky",100l,Arrays.asList("苹果"+new Random().nextInt())),
            new Apple("shinaizi",80l,Arrays.asList("苹果"+new Random().nextInt())),
            new Apple("shinaizi",80l,Arrays.asList("苹果"+new Random().nextInt())),
            new Apple("sherry",120l,Arrays.asList("苹果"+new Random().nextInt())),
            new Apple("lushangyouni",100l,Arrays.asList("苹果"+new Random().nextInt())));
    private static JSONObject getJsonObject(Apple apple){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("applePlace", apple.getPlace());
        return jsonObject;
    }
    public static void main(String[] args) throws IOException {
        JSONObject collect = apples.stream()
                .collect(JSONObject::new,
                        (parent, apple) -> parent.put(apple.getName(),getJsonObject(apple)),
                        JSONObject::putAll);
        System.out.println(collect);
//        System.out.println(StreamLearn.class.getResource("/").getContent());
//        longParam(1_000_000);
//        streamOperateForEach();
//        streamOperateFilter();
//        streamOperateFilterAndDistinctAndCount();
//        streamOperateFilterAndMapAndCollect();
//        streamOperateFilterAndSkip();
//        streamOperateFilterAndLimit();
//        streamOperateFilterAndSorted();
//        streamOperateFlatMap();
//        streamOperateMatch();
//        streamOperateFind();
//        streamOperateReduce();

    }

    private static void longParam(long limit){
        System.out.println(limit);
    }

    // ---------------------------------------stream常用操作开始------------------------

    /**
     * void forEach(Consumer<? super T> action);
     * 无返回值的
     * 入参是一个Consumer函数
     */
    private static void streamOperateForEach(){
        // 循环打印apples结果
        apples.stream().forEach(System.out::println);
    }

    /**
     * Stream<T> filter(Predicate<? super T> predicate);
     * 入参是一个predicate函数 也就是一个判断条件
     * 返回一个传入对象类型的Stream对象
     */
    private static void streamOperateFilter(){
        // 过滤出重量大于100的苹果
        apples.stream().filter(apple->{
            return apple.getWeight()>100;
        }).forEach(System.out::println);
        apples.stream().filter(apple -> apple.getWeight()>100).forEach(System.out::println);;
    }

    /**
     *  <R> Stream<R> map(Function<? super T, ? extends R> mapper);
     *  入参是个function函数
     *  有返回值
     *  T  -- Apple
     *  R  -- getName的返回类型
     */
    private static void streamOperateFilterAndMap(){
        // 返回苹果的名字的类型的Stream流
        Stream<String> stringStream = apples.stream().map(Apple::getName);
    }

    /**
     *   一、 <R, A> R collect(Collector<? super T, A, R> collector);
     *          传入一个Collector对象
     *
     *
     *   二、 <R> R collect(Supplier<R> supplier,
     *                   BiConsumer<R, ? super T> accumulator,
     *                   BiConsumer<R, R> combiner);
     *                 supplier是返回对象的类型定义  比如想要返回一个   HashMap<String,Long> 创建此对象实例 以及对象参数类型
     *                  accumulator 收集器 则为返回对象的获取逻辑   即 map对象对应的string和Long的数据来源
     *                  combiner 组合器 将结果全部组合起来 即map的putall方法
     *
     */
    private static void streamOperateFilterAndMapAndCollect(){
        // 返回苹果的名字的类型的Stream流
        Stream<String> stringStream = apples.stream().map(Apple::getName);
        // 第一种转换成集合的方式
        List<String> collect1 = stringStream.collect(toList());
        System.out.println(collect1.getClass());
        /*
         public static <T>
            Collector<T, ?, List<T>> toList() {
                return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                           (left, right) -> { left.addAll(right); return left; },
                                           CH_ID);
            }
        * */

        //----------------------方式二---------------------------------------
        Stream<Apple> stream = apples.stream();
//        ArrayList<String> collect = stream.collect(ArrayList<String>::new, (map, p) -> map.add(p.getName()), ArrayList::addAll);
        HashMap<String, Long> collect = stream.collect(HashMap<String, Long>::new, (map, p) -> map.put(p.getName(), p.getWeight()), HashMap::putAll);
        System.out.println(collect.getClass());
        System.out.println(collect);
    }
    /**
     * 去重+数量
     * Stream<T> distinct();
     * long count();
     */
    private static void streamOperateFilterAndDistinctAndCount(){
        System.out.println("去重的:"+apples.stream().map(Apple::getName).distinct().count());
        System.out.println("未去重的:"+apples.stream().map(Apple::getName).count());

    }
    /**
     * Stream<T> skip(long n);
     * 跳过n个元素
     */
    private static void streamOperateFilterAndSkip(){
        apples.stream().skip(2).collect(toList()).forEach(x->{
            System.out.println(x);
        });
    }
    /**
     *  Stream<T> limit(long maxSize);
     * 只取maxSize个元素
     */
    private static void streamOperateFilterAndLimit(){
        apples.stream().limit(2).collect(toList()).forEach(x->{
            System.out.println(x);
        });
    }
    /**
     *
     *  Stream<T> sorted();
     *  无入参  需要比较的对象实现Comparable接口
     *
     *  Stream<T> sorted(Comparator<? super T> comparator);
     *  有入参 需要传入一个比较器
     *
     */
    private static void streamOperateFilterAndSorted(){
        apples.stream().map(Apple::getName).collect(Collectors.toList()).forEach(x->{
            System.out.println(x);
        });
        System.out.println("----------- 下面本来会异常-- 后来实现了implements Comparable<Apple>接口");
        apples.stream().sorted().collect(Collectors.toList()).forEach(x->{
            System.out.println(x);
        });
        System.out.println("-----------");
        apples.stream().map(Apple::getName).sorted().collect(Collectors.toList()).forEach(x->{
            System.out.println(x);
        });
        System.out.println("-----------");
        apples.stream().sorted(Comparator.comparing(Apple::getName)).collect(Collectors.toList()).forEach(x->{
            System.out.println(x);
        });
    }
    /**
     * <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
     * 入参是个function函数
     *    T是apples.stream()流对象中的具体的一个对象T
     *          R 是 具体对象T的Stream流
     *    用于将Apples集合中的某个内部对象 串联为一个流 方便操作
     */
    private static void streamOperateFlatMap(){
        // 打印所有苹果的别名
            Stream<String> stringStream = apples.stream().flatMap(apple -> apple.getPlace().stream());
        stringStream.forEach(System.out::println);
    }

    /**
     * match操作
     *
     *  boolean anyMatch(Predicate<? super T> predicate);
     *   boolean allMatch(Predicate<? super T> predicate);
     *    boolean noneMatch(Predicate<? super T> predicate);
     *
     *   入参均为 Predicate函数  传入一个判断条件
     */
    private static void streamOperateMatch() {
        // 只要有重量>100的即为true
        System.out.println(apples.stream().anyMatch(a -> a.getWeight() > 100));
        // 只有重量全部>100的即为true
        System.out.println(apples.stream().allMatch(a -> a.getWeight() > 100));
        // 重量没有一个>100的即为true
        System.out.println(apples.stream().noneMatch(a -> a.getWeight() > 100));

    }
    /**
     * find操作
     *  Optional<T> findAny();
     * Optional<T> findFirst();
     * 返回Optional对象
     */
    private static void streamOperateFind() {
        // 查找任意一个结果出来 如果存在即打印
        apples.stream().findAny().ifPresent(System.out::println);
        // 查找第一个结果出来 如果存在即打印
        apples.stream().findFirst().ifPresent(System.out::println);;
    }
    /**
     * reduce操作
     * 一、Optional<T> reduce(BinaryOperator<T> accumulator);
     *    传入一个BinaryOperator操作(二元操作)
     *    返回传入对象类型T的结果Optional
     *
     * 二、 T reduce(T identity, BinaryOperator<T> accumulator);
     *     identity  恒等式 (返回类型)  返回对象的初始值(默认值) 即肯定有值
     *     accumulator  收集器，这里是个二元操作对象
     *三、 <U> U reduce(U identity,
     *                  BiFunction<U, ? super T, U> accumulator,
     *                  BinaryOperator<U> combiner);
     *     identity 恒等式
     *     accumulator 收集器 这里是BiFunction对象
     *     combiner  组合器 与accumulator需要保持兼容  即结果保持一致
     *
     *     默认stream()的方式 只执行 accumulator
     *     只有在parallelStream的方式下，accumulator和combiner才会同时执行
     *
     */
    private static void streamOperateReduce() {
        // 对苹果的重量求和 方式一
        Optional<Long> reduce1 = apples.stream().map(Apple::getWeight).reduce((x, y) -> x + y);
        reduce1.ifPresent(System.out::println);
        // 对苹果的重量求和 方式二
        Long reduce2 = apples.stream().map(Apple::getWeight).reduce(0l, (x, y) -> x + y);
        System.out.println(reduce2);
        Long reduce21 = apples.stream().map(Apple::getWeight).reduce(0l, Long::sum);
        System.out.println(reduce21);
        // 对苹果的重量求和之后再平方 方式三
//        Long reduce30 = apples.stream().map(Apple::getWeight).reduce(0l,Long::sum, (x, y) -> x * y);
        Long reduce30 = apples.stream().map(Apple::getWeight).reduce(0l,(x, y) -> {
            System.out.println("30=accumulator");
            return x + y;
        }, (x, y) -> {
            System.out.println("30=combiner");
            return x * y;
        });
        Long reduce31 = apples.parallelStream().map(Apple::getWeight).reduce(0l,(x, y) -> {
//            System.out.println("31====accumulator");
            return x + y;
        }, (x, y) -> {
//            System.out.println("31====combiner");
            return x * y;
        });
        System.out.println(reduce30); // 480
        System.out.println(reduce31); // 7680000000
    }


    // ---------------------------------------stream常用操作结束------------------------


    // ---------------------------------------stream生成方式开始------------------------
    /**
     * 从集合中创建流对象
     * */
    private static Stream<String> createStreamFromCollection() {
        List<String> list = Arrays.asList("hello", "baigt", "ricky", "world", "stream");
        return list.stream();
    }

    /**
     * 从值中创建流
     * @return
     */
    private static Stream<String> createStreamFromValues() {
        return Stream.of("hello", "baigt", "ricky", "world", "stream");
    }

    /**
     * arrays方式创建流
     * @return
     */
    private static Stream<String> createStreamFromArrays() {
        String[] strings = {"hello", "baigt", "ricky", "world", "stream"};
        return Arrays.stream(strings);
    }

    /**
     * 从文件创建流
     * @return
     */
    private static Stream<String> createStreamFromFile() {
        Path path = Paths.get(StreamLearn.class.getResource("/")+StreamLearn.class.getName());
        try (Stream<String> streamFromFile = Files.lines(path)) {
            streamFromFile.forEach(System.out::println);
            return streamFromFile;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * iterate方式生成流
     * @return
     */
    private static Stream<Integer> createStreamFromIterator() {

        Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
        return stream;
    }

    /**
     * generate方式生成流
     * @return
     */
    private static Stream<Double> createStreamFromGenerate() {
        return Stream.generate(Math::random).limit(10);
    }
    /**
     * generate方式生成流
     * @return
     */
    private static Stream<Obj> createObjStreamFromGenerate() {
        return Stream.generate(new ObjSupplier()).limit(10);
    }

    // ---------------------------------------stream生成方式结束------------------------


    static class ObjSupplier implements Supplier<Obj> {
        private int index = 0;
        private Random random = new Random(System.currentTimeMillis());
        @Override
        public Obj get() {
            index = random.nextInt(100);
            return new Obj(index, "Name->" + index);
        }
    }

    static class Obj {
        private int id;
        private String name;

        public Obj(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "Obj{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    '}';
        }
    }
}
